Skip to content

v1.34.1.0 fix: gstack-update-check resists stale GitHub raw CDN + adds semver-order guard#1475

Merged
garrytan merged 2 commits into
mainfrom
garrytan/update-check-stale-cdn
May 13, 2026
Merged

v1.34.1.0 fix: gstack-update-check resists stale GitHub raw CDN + adds semver-order guard#1475
garrytan merged 2 commits into
mainfrom
garrytan/update-check-stale-cdn

Conversation

@garrytan
Copy link
Copy Markdown
Owner

@garrytan garrytan commented May 13, 2026

Summary

Two bugs in bin/gstack-update-check surfaced when running /gstack-upgrade right after v1.34.0.0 landed:

  • Branch-pinned raw URL (raw.githubusercontent.com/.../main/VERSION) was serving stale 1.33.2.0 for several minutes after the merge. --force cleared the local cache but happily wrote UP_TO_DATE 1.33.2.0 from the stale upstream and exited silently.
  • After the manual upgrade, the CDN was still stale. The script then emitted UPGRADE_AVAILABLE 1.34.0.0 1.33.2.0, telling the user to "upgrade" to an older version. No semver-order check existed.

This PR ships both fixes in one focused diff.

Fix 1: SHA-pinned VERSION fetch. bin/gstack-update-check now runs git ls-remote https://github.com/garrytan/gstack.git refs/heads/main to get the live HEAD SHA, then curls raw.githubusercontent.com/garrytan/gstack/<SHA>/VERSION. SHA-pinned raw URLs are immediately consistent — no branch-raw CDN lag. Branch-pinned URL kept as a fallback for offline / no-git / mirror cases. New GSTACK_REMOTE_REPO env var lets tests and private mirrors override the git URL.

Fix 2: Semver-order guard. After fetching REMOTE, the script runs printf '%s\n%s\n' "$LOCAL" "$REMOTE" | sort -V | tail -1 to confirm REMOTE > LOCAL. When LOCAL is at or ahead of REMOTE, it caches UP_TO_DATE and exits silently. This protects against transient stale-CDN responses AND dev installs running ahead of main.

Hardening: git ls-remote is fenced with GIT_TERMINAL_PROMPT=0 + GIT_HTTP_LOW_SPEED_LIMIT=1000 + GIT_HTTP_LOW_SPEED_TIME=5 so a flaky network or captive portal can't hang every skill preamble.

Test surface preservation: Existing tests use GSTACK_REMOTE_URL=file://... fixtures. The script now skips the ls-remote SHA path when GSTACK_REMOTE_URL is explicitly set, so all 35 existing tests pass unchanged.

Test Coverage

browse/test/gstack-update-check.test.ts — 35 existing tests + 3 new semver-guard tests.

New tests cover:

  • remote older than local (stale CDN) → silent, cache UP_TO_DATE
  • multi-segment sort: 1.9.0.0 < 1.10.0.0 → UPGRADE_AVAILABLE
  • multi-segment reverse sort: 1.10.0.0 > 1.9.0.0 → no rewind

Tests: 35 → 38 (+3 new). All green in 1.65s.

Pre-Landing Review

Pre-Landing Review: No issues found.

Manual adversarial pass on my own diff caught one real concern before commit: git ls-remote has no built-in timeout. Without GIT_HTTP_LOW_SPEED_*, a flaky network would hang every skill preamble. Fixed by fencing the call with the timeout env vars. Verified the test suite still passes after the addition.

Surfaces reviewed:

  • Command injection: _REMOTE_SHA validated against ^[0-9a-f]{40}$ before any URL interpolation. SHA cannot inject path traversal or query params.
  • Trust boundary: GSTACK_REMOTE_REPO and GSTACK_REMOTE_URL are user-controlled but only used as git ls-remote and curl targets. GIT_TERMINAL_PROMPT=0 prevents credential prompts on malicious URLs.
  • Locale: sort -V is available on both GNU and BSD coreutils, handles 4-segment versions consistently.

Design Review

No frontend files changed — design review skipped.

Eval Results

No prompt-related files changed — evals skipped.

Plan Completion

No plan file detected — this was a focused hotfix discovered live during /gstack-upgrade immediately after v1.34.0.0 merged.

TODOS

No TODO items completed in this PR.

Verification Results

End-to-end verified against real GitHub state:

$ SHA=$(git ls-remote https://github.com/garrytan/gstack.git refs/heads/main | awk '{print $1}')
$ echo "$SHA"
0c88517a0fc96f9776542b0cafe29e7982b87e30
$ curl -sf https://raw.githubusercontent.com/garrytan/gstack/$SHA/VERSION
1.34.0.0

After applying the patch to the global install at ~/.claude/skills/gstack/, gstack-update-check --force correctly reports UP_TO_DATE (LOCAL and REMOTE both at the live HEAD). Test suite reproduces every failure mode against file:// fixtures without touching the network.

Documentation

Documentation is current. No README / CLAUDE.md / ARCHITECTURE / CONTRIBUTING references to gstack-update-check internals, and the new GSTACK_REMOTE_REPO env var is documented in-script.

Test plan

  • bun test browse/test/gstack-update-check.test.ts — 38 pass, 0 fail
  • Manual end-to-end: gstack-update-check --force against real GitHub returns UP_TO_DATE
  • Tested SHA-pinned curl against raw.githubusercontent.com/<owner>/<repo>/<SHA>/VERSION returns the live VERSION
  • Verified semver guard suppresses backwards UPGRADE_AVAILABLE lines

🤖 Generated with Claude Code


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

garrytan and others added 2 commits May 13, 2026 10:20
Replace branch-raw fetch with git ls-remote + SHA-pinned raw URL. Add
semver-order guard via sort -V so REMOTE < LOCAL stays silent instead
of emitting a backwards UPGRADE_AVAILABLE line. Fence git ls-remote
with GIT_TERMINAL_PROMPT=0 + 5s low-speed timeout. Honor explicit
GSTACK_REMOTE_URL overrides for test fixtures and private mirrors.

3 new tests cover stale-CDN regression, multi-segment 1.9 vs 1.10
both directions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

E2E Evals: ✅ PASS

0/0 tests passed | $0 total cost | 12 parallel runners

Suite Result Status Cost

12x ubicloud-standard-2 (Docker: pre-baked toolchain + deps) | wall clock ≈ slowest suite

@garrytan garrytan merged commit 386fe51 into main May 13, 2026
23 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant